--[[

   Hyperloop Mod
   =============

   Copyright (C) 2017-2019 Joachim Stolberg

   LGPLv2.1+
   See LICENSE.txt for more information

]]--

-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta

-- Load support for intllib.
local S = vents.S
local NS = vents.NS

local Tube = vents.Tube
local Stations = vents.Stations

-- Used to store the Station list for each vent:
--    tStationList[SP(pos)] = {pos1, pos2, ...}
local tStationList = {}

local function store_station(pos, placer)
   local facedir = vents.get_facedir(placer)
   -- do a facedir correction
   facedir = (facedir + 3) % 4  -- face to LCD
   Stations:set(pos, 'Station', {
         owner = placer:get_player_name(),
         facedir = facedir,
         time_blocked = 0})
end

local function naming_formspec(pos)
   local data = Stations:get(pos)
   local vent = data.name or 'vent'
   local info = data.booking_info or ''
   local formspec =
      'size[7,4.4]'..
      'label[0,0;'..S('Please enter the vent name.')..']' ..
      'label[0,.35;Punch the vent to see locations you can teleport too.]' ..
      'field[0.2,1.5;7.1,1;name;'..S('Vent name')..';'..vent..']' ..
      'field[0.2,2.7;7.1,1;info;'..S('Additional vent information')..';'..info..']' ..
      'button_exit[2.5,3.7;2,1;exit;Save]'
   return formspec
end

-- Form spec for the station list
local function generate_string(sortedList)
   local tRes = {'size[12,8]'..
   'label[4,0; '..S('Select your destination')..']'}
   tRes[2] = 'tablecolumns[text,width=20;text,width=6,align=right;text]'

   local stations = {}
   for idx,tDest in ipairs(sortedList) do
      local name = tDest.name or S('<unknown>')
      local distance = tDest.distance or 0
      local info = tDest.booking_info or ''

      stations[#stations+1] = minetest.formspec_escape(string.sub(name, 1, 28))
      stations[#stations+1] = distance..'m'
      stations[#stations+1] = minetest.formspec_escape(info)
   end

   if #stations>0 then
      tRes[#tRes+1] = 'table[0,1;11.8,7.2;button;'..table.concat(stations, ',')..']'
   else
      tRes[#tRes+1] = 'button_exit[4,4;3,1;button;Update]'
   end

   return table.concat(tRes)
end

local function store_station_list(pos, sortedList)
   local tbl = {}
   for idx,item in ipairs(sortedList) do
      tbl[#tbl+1] = item.pos
   end
   tStationList[SP(pos)] = tbl
end

local function remove_junctions(sortedList)
   local tbl = {}
   for idx,item in ipairs(sortedList) do
      if not item.junction and item.booking_pos then
         tbl[#tbl+1] = item
      end
   end
   return tbl
end

local function filter_subnet(sortedList, subnet)
   if vents.subnet_enabled then
      if subnet == '' then
         subnet = nil
      end

      local tbl = {}
      for idx,item in ipairs(sortedList) do
         if item.subnet == subnet then
            tbl[#tbl+1] = item
         end
      end
      return tbl
   end
   return sortedList
end

-- Used to update the station list for booking machine
-- and teleport list.
local function station_list_as_string(pos, subnet)
   local meta = M(pos)
   -- Generate a name sorted list of all connected stations
   local sortedList = Stations:station_list(pos, pos, 'name')
   -- remove all junctions from the list
   sortedList = remove_junctions(sortedList)
   -- use subnet pattern to reduce the list
   sortedList = filter_subnet(sortedList, subnet)
   -- store the list for later use
   store_station_list(pos, sortedList)
   -- Generate the formspec string
   return generate_string(sortedList)
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
   local name = player:get_player_name()
   local pos = vents.player_pos[name]
   if formname == 'vents:setup' then
      if fields.exit then
         if fields.name ~= nil then
            local station_name = string.trim(fields.name)
            if station_name == '' then
               return
            end
            --if Stations:get(pos).booking_pos then
            --   minetest.chat_send_player(name, S('Remove and replace to change name.'))
               --Figure out how to allow a builder to update station.
            --   return
            --end
               -- store meta and generate station formspec
            Stations:update(pos, {
                  name = station_name,
                  booking_pos = pos,
                  booking_info = string.trim(fields.info),
            })

            local meta = minetest.get_meta(pos)
            meta:set_string('infotext', 'Station: '..station_name)
            minetest.chat_send_player(name, 'Saving data')
         else
            vents.chat(player, S('Invalid station name!'))
         end
      end
   elseif formname == 'vents:station_list' then
      local te = minetest.explode_table_event(fields.button)
      local idx = tonumber(te.row)
      if idx and te.type=='CHG' then
         local tStation, src_pos = vents.get_base_station(pos)
         local dest_pos = tStationList[SP(pos)] and tStationList[SP(pos)][idx]
         if dest_pos and tStation then
            local air = minetest.find_node_near(dest_pos, 2, {'air'})
            if air then
               player:set_pos(air)
            else
               minetest.chat_send_player(name, 'Sorry, can\'t teleport you there.')
            end
            --Should there be some sort of freeze on movement or health loss for using the vents?
         end
         minetest.close_formspec(player:get_player_name(), formname)
      end
   end
end)

local function on_destruct(pos)
   Stations:update((pos), {
      booking_pos = 'nil',
      booking_info = 'nil',
      name = 'Station',
   })
end

minetest.register_node('vents:station', {
   description = S('Vent Access'),
   drawtype = 'nodebox',
   tiles = {'vents_station.png'},

   after_place_node = function(pos, placer, itemstack, pointed_thing)
      vents.check_network_level(pos, placer)
      M(pos):set_string('infotext', S('Access Point'))
      store_station(pos, placer)
      Tube:after_place_node(pos)
   end,

   on_rightclick = function(pos, node, clicker)
      local name = clicker:get_player_name()
      local player_attributes = clicker:get_meta()
      local mode = player_attributes:get_string('mode')
      vents.player_pos[name] = pos
      if mode == 'traitor' or mode == 'solo' or mode == 'ghost' then
         minetest.show_formspec(name, 'vents:station_list', station_list_as_string(pos))
      elseif mode == 'builder' then
         minetest.show_formspec(name, 'vents:setup', naming_formspec(pos))
      else
         minetest.chat_send_player(name, 'Sorry, you can\'t use this.')
      end
   end,

   on_punch = function(pos, node, puncher)
      local wield = puncher:get_wielded_item()
      local wield_name = wield:get_name()
      if wield_name == 'creative:tool_breaking' then
         return
      else
         local name = puncher:get_player_name()
         local player_attributes = puncher:get_meta()
         local mode = player_attributes:get_string('mode')
         vents.player_pos[name] = pos
         if mode == 'builder' then
            minetest.show_formspec(name, 'vents:station_list', station_list_as_string(pos))
         end
      end
   end,

   after_dig_node = function(pos, oldnode, oldmetadata, digger)
      Tube:after_dig_node(pos)
      Stations:delete(pos)
   end,

   on_destruct = on_destruct,
   on_rotate = screwdriver.disallow,
   paramtype = 'light',
   paramtype2 = 'facedir',
   groups = {breakable=1},
   is_ground_content = false,
   sounds = {footstep = {name = 'metal', gain = 1}},
})
